// NotificationsService.cs
// Facebook/Framework/Service
// Copyright (c) 2007, Nikhil Kothari. All Rights Reserved.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Facebook.Service.Core;

namespace Facebook.Service {

    /// <summary>
    /// Represents the service APIs related to the user's notifications on Facebook.
    /// </summary>
    public sealed class NotificationsService {

        private FacebookSession _session;

        internal NotificationsService(FacebookSession session) {
            Debug.Assert(session != null);
            _session = session;
        }

		/// <summary>
		/// This takes a collection of strings and transforms it into a comma-delimited list.
		/// </summary>
		/// <param name="listOfItems">Collection of strings to transform.</param>
		/// <returns>StringBuilder containing the comma-delimited list of items.</returns>
		private StringBuilder CreateCommaDelimitedList(ICollection<string> listOfItems)
		{
			StringBuilder listBuilder = new StringBuilder();
			foreach (string item in listOfItems)
			{
				if (listBuilder.Length != 0)
				{
					listBuilder.Append(",");
				}
				listBuilder.Append(item);
			}
			return listBuilder;
		}

		/// <summary>
		/// Sends an email to the specified users, who must have added your application.
		/// You can send five emails to a user per day.
		/// </summary>
		/// <remarks>
		/// Requires a session key for desktop applications, which may only send email to
		/// the person whose session it is. This method does not require a session for
		/// Web applications.
		/// </remarks>
		/// <param name="recipients">
		/// Collection of user IDs to whom to attempt to send an email, anything over 100
		/// will be batched in groups of 100. Each batch is processed sequentially, so it
		/// will take more time the more recipients you send. It must also be users who have
		/// added your application, whatever that means in the new Facebook context.
		/// </param>
		/// <param name="subject">The subject of the email message.</param>
		/// <param name="fbmlBody">
		/// The FBML version of the email. You must include at least one of either this or the
		/// <paramref name="textBody"/> parameters. This parameter takes a stripped-down set
		/// of FBML that allows only tags that result in text, links and linebreaks.
		/// </param>
		/// <param name="textBody">
		/// The plain text version of the email content. You must include at least one of
		/// either this or the <paramref name="fbmlBody"/> parameters.
		/// </param>
		/// <returns>
		/// Returns a string array of the user IDs of recipients to whom the email was successfully sent.
		/// </returns>
		public string[] SendEmail(ICollection<string> recipients, string subject, string fbmlBody, string textBody)
		{
			int batches = Convert.ToInt32(Math.Ceiling(recipients.Count / 100.0));
			List<List<string>> recipientBatches = new List<List<string>>(batches);
			List<string> tempBatch = new List<string>(100);
			foreach (string recipient in recipients)
			{
				// Create a batch every 100 recipients
				if (tempBatch.Count == 100)
				{
					// Add it to the batches (create a new list to avoid passing by reference)
					recipientBatches.Add(new List<string>(tempBatch));
					// Clear it out and start fresh
					tempBatch.Clear();
				}
				tempBatch.Add(recipient);
			}
			// This conditional takes care of the situation where recipients mod 100 is zero
			if (tempBatch.Count > 0)
			{
				// Add the last batch, which should be less than 100
				recipientBatches.Add(new List<string>(tempBatch));
			}
			List<string> successfulRecipients = new List<string>(recipients.Count);

			// Loop over the batches, sending a request for each one
			// TODO: Make this into a batch call whenever that functionality is added to the framework
			foreach (ICollection<string> batch in recipientBatches)
			{
				// Session key required for desktop applications only
				FacebookRequest request = new FacebookRequest(_session, (_session.ApplicationType == FacebookApplicationType.Client));
				request.Parameters["recipients"] = CreateCommaDelimitedList(batch).ToString();
				request.Parameters["subject"] = subject;
				if (String.IsNullOrEmpty(fbmlBody))
				{
					request.Parameters["text"] = textBody;
				}
				else
				{
					request.Parameters["fbml"] = fbmlBody;
				}

				FacebookResponse response = request.InvokeMethod("notifications.sendEmail");
				if (response.Status == FacebookResponseStatus.Succeeded)
				{
					if (response.Result is string)
					{
						string[] successes = ((string)response.Result).Split(',');
						successfulRecipients.AddRange(successes);
					}
				}
			}
			
			return successfulRecipients.ToArray();
		}

		/// <summary>
		/// Sends a notification to the specified set of users.
		/// </summary>
		/// <remarks>
		/// The default notification type will be "General."
		/// </remarks>
		/// <param name="userID">The ID of the user to notify.</param>
		/// <param name="notification">FBML for the notifications page. Uses a stripped down version allowing only text and links.</param>
		public void SendNotification(string userID, string notification)
		{
			if (String.IsNullOrEmpty(userID))
			{
				throw (new ArgumentNullException("userID"));
			}

			SendNotification(new string[] { userID }, notification, NotificationTypes.General);
		}
		/// <summary>
		/// Sends a notification to the specified set of users.
		/// </summary>
		/// <remarks>
		/// The default notification type will be "General."
		/// </remarks>
		/// <param name="userIDs">The ID of the users to notify.</param>
		/// <param name="notification">FBML for the notifications page. Uses a stripped down version allowing only text and links.</param>
		public void SendNotification(ICollection<string> userIDs, string notification)
		{
			if ((userIDs == null) || (userIDs.Count == 0))
			{
				throw new ArgumentNullException("userIDs");
			}
			if (String.IsNullOrEmpty(notification))
			{
				throw new ArgumentNullException("notification");
			}

			SendNotification(userIDs, notification, NotificationTypes.General);
		}
		/// <summary>
		/// Sends a notification to the specified set of users.
		/// </summary>
		/// <remarks>
		/// This overload is here for backwards-compatibility. Please use the non-obsolete method
		/// for the most flexibility. The default notification type will be "General."
		/// </remarks>
		/// <param name="userID">The ID of the user to notify.</param>
		/// <param name="notification">FBML for the notifications page. Uses a stripped down version allowing only text and links.</param>
		/// <param name="email">This is ignored. If you need to send email, please use <see cref="SendEmail"/>.</param>
		/// <returns>Empty string since the original return value has since been deprecated.</returns>
		[Obsolete("The email parameter has been deprecated, use SendNotification(userIDs, notification, notificationType) instead.")]
        public string SendNotification(string userID, string notification, string email) {
            if (String.IsNullOrEmpty(userID)) {
                throw new ArgumentNullException("userID");
            }

			SendNotification(new string[] { userID }, notification, NotificationTypes.General);
			return String.Empty;
        }
        /// <summary>
        /// Sends a notification to the specified set of users.
        /// </summary>
		/// <remarks>
		/// This overload is here for backwards-compatibility. Please use the non-obsolete method
		/// for the most flexibility. The default notification type will be "General."
		/// </remarks>
        /// <param name="userIDs">The ID of the users to notify.</param>
		/// <param name="notification">FBML for the notifications page. Uses a stripped down version allowing only text and links.</param>
		/// <param name="email">This is ignored. If you need to send email, please use <see cref="SendEmail"/>.</param>
		/// <returns>Empty string since the original return value has since been deprecated.</returns>
		[Obsolete("The email parameter has been deprecated, use SendNotification(userIDs, notification, notificationType) instead.")]
        public string SendNotification(ICollection<string> userIDs, string notification, string email)
		{
			if ((userIDs == null) || (userIDs.Count == 0))
			{
				throw new ArgumentNullException("userIDs");
			}
			if (String.IsNullOrEmpty(notification))
			{
				throw new ArgumentNullException("notification");
			}

			SendNotification(userIDs, notification, NotificationTypes.General);
			return String.Empty;
		}
		/// <summary>
		/// Sends a notification to the specified set of users.
		/// </summary>
		/// <remarks>
		/// The user name of the logged-in user is prepended to the notification.
		/// Your notification FBML should take this into account by starting with
		/// a verb. This does not happen if you pass in an empty <paramref name="userIDs"/>.
		/// </remarks>
		/// <param name="userIDs">The ID of the users to notify.</param>
		/// <param name="notification">FBML for the notifications page. Uses a stripped down version allowing only text and links.</param>
		/// <param name="notificationType">Indicates which type of notification to send.</param>
		public void SendNotification(ICollection<string> userIDs, string notification, NotificationTypes notificationType)
		{
            if ((userIDs == null) || (userIDs.Count == 0)) {
                throw new ArgumentNullException("userIDs");
            }
            if (String.IsNullOrEmpty(notification)) {
                throw new ArgumentNullException("notification");
            }

			StringBuilder userIDListBuilder = CreateCommaDelimitedList(userIDs);

			// Announcement notifications do not require an active session, but general ones do.
            FacebookRequest request = new FacebookRequest(_session, (notificationType == NotificationTypes.General));
            request.Parameters["to_ids"] = userIDListBuilder.ToString();
            request.Parameters["notification"] = notification;
			request.Parameters["type"] = notificationType.ToString().ToLower();

            FacebookResponse response = request.InvokeMethod("notifications.send");
        }

        /// <summary>
        /// Sends a request or an invitation to the specified user.
        /// </summary>
        /// <param name="userID">The ID of the user to send a request.</param>
        /// <param name="requestType">The type of request.</param>
        /// <param name="content">The content of the request.</param>
        /// <param name="imageURL">The image associated with the request.</param>
        /// <param name="isInvitation">Whether the request is an invitation.</param>
        /// <returns>A URL to redirect to if a confirmation is required.</returns>
		[Obsolete("Function disabled! See http://wiki.developers.facebook.com/index.php/Notifications.sendRequest", true)]		
        public string SendRequest(string userID, string requestType, string content, string imageURL, bool isInvitation) {
            if (String.IsNullOrEmpty(userID)) {
                throw new ArgumentNullException("userID");
            }

            return SendRequest(new string[] { userID }, requestType, content, imageURL, isInvitation);
        }

        /// <summary>
        /// Sends a request or an invitation to the specified set of users.
        /// </summary>
        /// <param name="userIDs">The ID of the users to send a request.</param>
        /// <param name="requestType">The type of request.</param>
        /// <param name="content">The content of the request.</param>
        /// <param name="imageURL">The image associated with the request.</param>
        /// <param name="isInvitation">Whether the request is an invitation.</param>
        /// <returns>A URL to redirect to if a confirmation is required.</returns>
		[Obsolete("Function disabled! See http://wiki.developers.facebook.com/index.php/Notifications.sendRequest", true)]
        public string SendRequest(ICollection<string> userIDs, string requestType, string content, string imageURL, bool isInvitation) {

			throw new NotSupportedException("Marked as obsolete by Facebook.  See http://wiki.developers.facebook.com/index.php/Notifications.sendRequest");
        }
    }
}